package com.goingup.domain;
import com.goingup.domain.Vector.Direction;



/**
 * Represents an Elevator.  It has passengers, a current floor number.  It opens and closes doors to allow
 * passengers on.  It takes a constant time to move between floors currently. 
 * @author pcresswell
 *
 */
public class Elevator implements Runnable, Requestable{
	
	private Passengers passengers = new Passengers();
	private ElevatorDoors doors = new ElevatorDoors();
	private RequestQueue requestQueue = new RequestQueue();
	private Vector vector;
	private Mode mode = Mode.ACTIVE;
	private int capacity=10;
	private enum Mode  {
		ACTIVE,
		INACTIVE,
		SERVICE,
		FIRE
	}
	
	public Elevator(Floor initialFloor){
		vector = new Vector(initialFloor);
	}
	public boolean atCapacity()
	{
		return (this.getPassengerCount()==capacity);
	}
	
	/**
	 * Passenger boards and requests a drop off at their destination floor.
	 * @param passenger
	 * @throws MaximumCapacityExceededException 
	 */
	protected void addPassenger(Passenger passenger) throws MaximumCapacityExceededException {
		if (atCapacity())
			throw new MaximumCapacityExceededException();
		
		passengers.add(passenger);
		System.out.print("Passenger got on.  Passenger count is: " + getPassengerCount() + "\n");
	}
	/**
	 * Number of passengers currently on the elevator.
	 * @return
	 */
	public int getPassengerCount() {
		return passengers.size();
	}
	
	/**
	 * Numerical floor number that the elevator is currently on.
	 * @return
	 */
	public int getCurrentFloorNumber() {
		return vector.getCurrentFloor().getNumber();
	}
	
	private void moveToFloor(Floor targetFloor) {
		vector.setTargetFloor(targetFloor);
				
		while(!vector.atTargetFloor()){
			vector.move();
			checkForPassengersAtCurrentFloor();
		}
	}

	private void checkForPassengersAtCurrentFloor() {
		if (!requestQueue.hasPickupRequestForFloor(vector.getCurrentFloor(), vector.getCurrentDirection()))
			return;
		PickUpRequest request = requestQueue.getPickupRequestForFloor(vector.getCurrentFloor(), vector.getCurrentDirection());	
		boardPassengers();
		request.executed();
		
	}
	private void boardPassengers() {
		if (thereArePassengersGettingOffAtCurrentFloor())
			doors.open(); //people getting off
		else if (thereArePassengersAtCurrentFloorGettingOn())
			doors.open(); //people getting on
		else
			return; //no one getting on or off
		
		letAllPassengersOff();
		letAllPassengersOn();
		
		doors.close();
	}

	private boolean thereArePassengersAtCurrentFloorGettingOn() {
		return vector.getCurrentFloor().hasPassengers();
	}

	private boolean thereArePassengersGettingOffAtCurrentFloor() {
		return passengers.hasPassengersGettingOffAt(vector.getCurrentFloor());
	}
	private void letAllPassengersOn() {	
		vector.getCurrentFloor().boardPassengers(this);
	}
	private void letAllPassengersOff() {
		passengers.removePassengersGettingOffAt(vector.getCurrentFloor());
	}
	
	protected void requestDropOffOn(Floor floorRequested){
		if (floorRequested==null)
			return;
		requestQueue.addDropOffRequest(new DropOffRequest(floorRequested));
	}
	protected synchronized void handlePickupRequest(PickUpRequest request){
		moveToFloorAndBoard(request.getFloor());
	}
	protected synchronized void handleDropOffRequest(DropOffRequest dropOffRequest){	
		moveToFloorAndBoard(dropOffRequest.getFloor());
	}

	private void moveToFloorAndBoard(Floor floor) {
		moveToFloor(floor);
		boardPassengers();
	}
	@Override
	public void run() {
		while(true)
			handleRequests();
	}
	private void handleRequests() {
		requestQueue.executeRequests(this);
	}

	public Floor getCurrentFloor() {
		return vector.getCurrentFloor();
	}

	public Direction getCurrentDirection() {
		return vector.getCurrentDirection();
	}

	public void addRequest(PickUpRequest pickUpRequest) {
		requestQueue.addPickUpRequest(pickUpRequest);
	}
	public int distanceFrom(Floor floor) {
		return vector.distanceFrom(floor);
	}
	public boolean acceptingRequests(){
		return (mode.equals(Mode.ACTIVE));
	}
	
	
	public void putInServiceMode()
	{
		mode=Mode.SERVICE;
	}
	public void putInFireMode()
	{
		mode=Mode.FIRE;
	}
	public void putInActiveMode()
	{
		mode=Mode.ACTIVE;
	}
	public void putInInactiveMode()
	{
		mode=Mode.INACTIVE;
	}

	public void remove(Passenger passenger) {
		passengers.remove(passenger);
	}

	
	
}
